Skip to content

[DWARF Import] Support relocatable objects: section addresses and RISC-V relocations - #8656

Closed
jrozner wants to merge 3 commits into
Vector35:devfrom
jrozner:dwarf-relocatable-objects
Closed

jrozner wants to merge 3 commits into
Vector35:devfrom
jrozner:dwarf-relocatable-objects

Conversation

@jrozner

@jrozner jrozner commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Depends on #8654, which this branch is based on. Until it merges, the diff also shows that commit. The two new commits make DWARF import work for relocatable objects (ET_REL).

1. Place object file addresses where the view put their sections

All sections of a .o have address 0, so a DWARF address is an offset into the section its relocation's symbol is in. The importer only added the image base, so anything outside the first section landed at the wrong address:

Object Item Before After (matches the view's symbol)
x86-64 -ffunction-sections -fdata-sections bump, sum_table, main all 0x400000 0x400010, 0x400040, 0x400070
zstd zstd_compress.c 167 functions all at 0x400000 167 distinct, all right
x86-64 -g .bss variables unmapped offsets correct
x86-64 -g .data variables missing correct

Change:

  • For a relocatable debug file, look up each allocated section in the view by name (size must match; the ELF view's <name>#<index> duplicates resolve too).
  • RelocationMap::add adds that section's address to the addend of relocations against its symbols, so relocated DWARF addresses, including .debug_frame, land where the view put the section.
  • Linked executables, shared objects, the .gnu_debugaltlink file and dwarfdump get an empty map, so they are unchanged.
  • [DWARF Import] Fix addresses for PIE and rebased views, relocatable objects and RISC-V objects #8654 skips rebasing data variables for objects because their addresses were section offsets. They are placed now, so that exception is removed.

-fcommon symbols have no section and are not relocated.

2. Apply RISC-V label-difference relocations

Any RISC-V object built with -g fails with Failed to parse DWARF: Failed to load DWARF info: Unsupported relocation type, because object only accepts absolute relocations in a relocation map. With linker relaxation, GNU as emits pairs at the same offset instead (ADD/SUB 8-64, SET6/SUB6, SET8/SUB8, SET_ULEB128/SUB_ULEB128). They can't go in a map, since SUB* reads what ADD*/SET* wrote.

Change: on RISC-V, RelocationMap::add applies these in file order to a copy of the section data, following the psABI formulas and binutils (perform_relocation; the SUB_ULEB128 addend is ignored, as in binutils PR31179). Errors if a ULEB128 result doesn't fit its encoded length or a SET_ULEB128 isn't followed by its SUB_ULEB128 (Error::RelocationOutOfBounds, Error::InvalidRelocation). NONE and RELAX are skipped. R_RISCV_32/64 still go through the map.

Each pair names two labels in one section, so the difference doesn't depend on where the section is placed and S is the symbol's offset in its section.

Testing

Loaded headless with this dwarf_import in place of the bundled one (corePlugins.dwarfImport: false):

  • Section addresses: x86-64 objects (-g, -ffunction-sections, COMDAT duplicates, zstd) have every function and data variable on its ELF symbol. This also holds with loader.imageBase = 0x800000.
  • RISC-V: an ESP32-C3 C corpus (Espressif GCC 14.2, -O2 and -Og) fails before and imports after: 20 functions, 2 data variables and 4 types, with all addresses on their symbols. Names, signatures, locals and types match the same object linked with ld --no-relax. Line rows, ranges, location lists and CFA rows match the linked ELF's.
  • No regressions: linked x86-64 PIE and non-PIE, and the ESP-IDF hello_world ELFs for the ESP32 and ESP32-C3, give the same results as [DWARF Import] Fix addresses for PIE and rebased views, relocatable objects and RISC-V objects #8654.
  • cargo fmt --check passes. Clippy shows the same warnings as dev and none in the new code.

References: RISC-V psABI relocation table and ULEB128 note; binutils bfd/elfnn-riscv.c.

post_process rebases every function address by the view's start minus
the file's original image base, but it left data variables at the
addresses from DW_OP_addr/DW_OP_addrx. When the view isn't at the
original image base (a PIE, which is loaded at 0x400000, or any file
opened with a non-default loader.imageBase), every data variable was
added at an unmapped address off by exactly that amount, and the ELF
symbols at the right addresses got no DWARF type.

Rebase data variables by the same amount, through a helper shared with
the function rebase. Relocatable objects are left as they were: their
addresses are offsets into sections that the view lays out one after
another, so the rebase alone would move their data variables from
unmapped addresses onto other sections' contents, such as code.
…d their sections

Every section of a relocatable ELF object is at address 0, so an address in
its DWARF info is an offset into the section its relocation's symbol is in.
The ELF view lays those sections out one after another from the image base,
but the importer only added the image base, so everything outside the first
section landed in the wrong place: with -ffunction-sections every function
was at the image base, and on Xtensa, where .literal precedes .text, every
function was 0x20 too low.

When the debug file is a relocatable ELF object, look up where the view put
each allocated section and add that to the relocations against symbols in
it. Linked files are unaffected.

With object addresses now relative to the image base like any other file's,
rebase their data variables too, and drop the exception that left them
unrebased.
…tions

Loading a RISC-V relocatable object built with -g failed with "Failed to
load DWARF info: Unsupported relocation type", so no debug info was
imported. object's RelocationMap only accepts absolute relocations, and
with linker relaxation GNU as encodes differences between code labels
as relocation pairs at the same offset: R_RISCV_ADD*/SUB* (high_pc,
line table and FDE lengths), R_RISCV_SET6/SUB6 and SET8/SUB8 (CFA
advances) and R_RISCV_SET_ULEB128/SUB_ULEB128 (location and range list
offsets).

Apply these to the section data before handing it to gimli, in order,
as binutils' perform_relocation does, following the psABI formulas. The
object isn't relaxed, so this gives the right difference. ULEB128 values
keep the length the assembler gave them, and SUB_ULEB128's addend is
ignored like binutils does. R_RISCV_NONE and R_RISCV_RELAX are skipped,
and R_RISCV_32/64 still go through the relocation map, which moves them
to where the view placed their section.

The pairs use the symbols' offsets into their sections as S, without the
section's address in the view: both labels of a pair are in the same
section, so their difference doesn't depend on where it was placed.
@jrozner

jrozner commented Oct 13, 2026

Copy link
Copy Markdown
Contributor Author

Closing: folded into #8654 so the DWARF changes are reviewed in one place.

@jrozner jrozner closed this Oct 13, 2026
@jrozner
jrozner deleted the dwarf-relocatable-objects branch October 13, 2026 02:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant